进阶 Jenkins 容器里面套 Docker & 操作宿主机 Docker & 权限问题
本节是进阶章节,涉及较多服务端知识。重点在于理解解决问题的思路,而非单纯记忆步骤。
问题背景
在 Docker 中运行 Jenkins 后,希望在 Jenkins 容器内使用 Docker 命令来构建镜像、运行容器。这引出了一个核心问题:如何让容器内的 Jenkins 操作 Docker?
两种主要方案
| 方案 | 名称 | 原理 | 适用场景 |
|---|---|---|---|
| DinD (Docker-in-Docker) | 嵌套 Docker | 在 Jenkins 容器内运行独立的 Docker Daemon | 构建、测试、制品推送 |
| DooD (Docker-outside-of-Docker) | 挂载宿主机 Docker | 将宿主机的 /var/run/docker.sock 挂载到容器 | 发布服务到宿主机、简化部署 |
坑点一:GLIBC 动态库版本冲突
问题复现
直接将宿主机的 /usr/bin/docker 映射到容器内:
docker run -d \
--name jenkins \
-v /usr/bin/docker:/usr/bin/docker \
-p 8080:8080 \
jenkins/jenkins:lts
bash
进入容器执行 docker version,报错:
error while loading shared libraries: libsystemd.so.0: version `GLIBC_2.35' not found
text
原因分析
使用 ldd 命令查看动态库依赖:
# 宿主机(Ubuntu 22.04)
ldd --version
# 输出: GLIBC 2.35
# Jenkins 容器(基于 Debian 11)
docker run --rm jenkins/jenkins:ltd ldd --version
# 输出: GLIBC 2.31
bash
宿主机的 Docker 二进制文件依赖 GLIBC 2.35,但 Jenkins 容器(基于 Debian 11)只有 GLIBC 2.31,导致版本不兼容。
三种解决方案对比
| 方案 | 操作 | 可行性 | 风险 |
|---|---|---|---|
| 降级操作系统 | 将 Ubuntu 22.04 降到 18.04 | 低 | 已有服务受影响,代价太大 |
| 升级容器基础镜像 | 将 Debian 11 升到更高版本 | 中 | 后续构建产物需匹配新版本 |
| DinD 方案(推荐) | 在容器内安装独立的 Docker | 高 | 需要 privileged 模式 |
坑点二:DinD 方案(官方推荐)
核心原理
Jenkins 官方英文文档推荐使用 docker:dind 镜像,在 Jenkins 容器旁启动一个独立的 Docker Daemon 容器,通过 TCP 协议通信,彻底避免 GLIBC 版本冲突。
架构示意:
┌─────────────────────────────────────────────┐
│ 宿主机 Docker │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Jenkins 容器 │ │ Docker-in-Docker │ │
│ │ (含 Docker CLI)│──TCP:2376──▶│ (docker:dind) │ │
│ │ │ │ 独立 Docker Daemon│ │
│ └──────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────┘
text
关键点:
- Jenkins 容器内安装的是 Docker CLI(客户端),不是完整的 Docker Daemon
- Docker CLI 通过
DOCKER_HOST=tcp://docker:2376环境变量找到 DinD 容器 - DinD 容器通过 Docker 网络别名
docker暴露服务
自定义 Jenkins Dockerfile
在 Jenkins 基础镜像上安装 Docker CLI,并配置国内加速源(坑点三):
# Dockerfile
# 适用于 Jenkins 2.419+
FROM jenkins/jenkins:2.419
USER root
# 使用腾讯云加速 Debian 源(国内环境必须)
RUN apt-get update && apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common
# 替换为腾讯云 Debian 镜像源
RUN sed -i 's|deb.debian.org|mirrors.cloud.tencent.com|g' /etc/apt/sources.list
# 安装 Docker CLI
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - && \
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \
apt-get update && \
apt-get install -y docker-ce-cli
USER jenkins
dockerfile
构建镜像:
docker build -t myjenkins:2.419-docker .
bash
Docker Compose 完整配置
# docker-compose.yml
# 适用于 x86 架构(Windows/Linux)
# ARM 架构(Apple Silicon)请将 docker:dind 替换为 arm64v8/docker:dind
networks:
jenkins:
external: true # 需要先手动创建: docker network create jenkins
volumes:
jenkins-docker-certs:
driver: local
jenkins-data:
driver: local
services:
# Docker-in-Docker 服务
jenkins-docker:
image: docker:dind
container_name: jenkins-docker
privileged: true
restart: unless-stopped
networks:
jenkins:
aliases:
- docker # 重要:必须设置别名,否则 TLS 证书校验失败
environment:
- DOCKER_TLS_CERTDIR=/certs # 启用 TLS(DinD 默认启用)
volumes:
- jenkins-docker-certs:/certs/client
- jenkins-data:/var/jenkins_home
# Jenkins 服务
jenkins-blueocean:
image: myjenkins:2.419-docker
container_name: jenkins-blueocean
restart: unless-stopped
networks:
- jenkins
environment:
- DOCKER_HOST=tcp://docker:2376 # 对应 DinD 的网络别名
- DOCKER_CERT_PATH=/certs/client # TLS 证书路径
- DOCKER_TLS_VERIFY=1 # 启用 TLS 验证
volumes:
- jenkins-docker-certs:/certs/client:ro # 只读挂载证书
- jenkins-data:/var/jenkins_home
- /home/jenkins/data:/var/jenkins_home # 映射到宿主机方便管理
ports:
- "10050:8080"
yaml
启动与验证
# 1. 创建外部网络
docker network create jenkins
# 2. 启动服务
docker compose up -d
# 3. 验证 Jenkins 容器内的 Docker
docker exec -it jenkins-blueocean docker version
# 应显示 Docker CLI 版本,不会报 GLIBC 错误
# 4. 验证 Docker-in-Docker 独立性
docker exec -it jenkins-blueocean docker ps
# 输出为空,说明 DinD 是独立环境,与宿主机隔离
bash
坑点三:国内构建镜像加速
自制 Jenkins 镜像时,Debian 的 apt 源在国内访问极慢。需要替换为国内镜像:
| 加速源 | 地址 |
|---|---|
| 腾讯云(推荐) | mirrors.cloud.tencent.com |
| 清华大学 | mirrors.tuna.tsinghua.edu.cn |
| 华为云 | mirrors.huaweicloud.com |
在 Dockerfile 中添加:
RUN sed -i 's|deb.debian.org|mirrors.cloud.tencent.com|g' /etc/apt/sources.list
dockerfile
DooD 方案(操作宿主机 Docker)
如果需要 Jenkins 直接操作宿主机 Docker(如发布服务),可挂载 Docker Socket:
docker run -d \
--name jenkins \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /home/jenkins/data:/var/jenkins_home \
-p 10050:8080 \
myjenkins:2.419-docker
bash
安全警告:挂载
docker.sock等于赋予容器 root 级别的宿主机权限。任何能执行 Docker 命令的进程都可以完全控制宿主机。仅适用于受信任的内部环境。
DinD 与 DooD 适用场景对比
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 构建/测试镜像 | DinD | 逻辑隔离,不影响宿主机 |
| 推送制品到仓库 | DinD | 构建环境与宿主机解耦 |
| 发布服务到宿主机 | DooD | 需要直接操作宿主机 Docker |
| 多团队共享 Jenkins | DinD | 安全隔离 |
权限问题补充
Jenkins 容器默认以 jenkins 用户运行,挂载的宿主机目录可能出现权限问题:
# 方法一:放宽目录权限(简单但有安全风险)
chmod -R 777 /home/jenkins/data
# 方法二:指定用户运行(推荐)
docker run -d \
--user $(id -u):$(id -g) \
-v /home/jenkins/data:/var/jenkins_home \
...
bash
注意事项
- 中英文文档差异:Jenkins 中文文档严重滞后,DinD 方案仅在英文官方文档中才有详细说明
- 网络别名:DinD 容器的网络别名
docker必须配置,否则 TLS 证书校验失败 - 端口映射:DinD 的 2376 端口在同一 Docker 网络内通信,不需要映射到宿主机
- 架构兼容:Apple Silicon(ARM)需使用
arm64v8/docker:dind镜像 - DooD 不适合发布服务到宿主机后再映射端口:需要两层端口映射,极其复杂
参考资料
↑